

<!DOCTYPE html>
<html lang="zh-CN" color-mode=light>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>redis - 唱跳rap</title>
  <meta name="apple-mobile-web-app-capable" content="yes" />
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="google" content="notranslate" />
  
  <meta name="description" content="Redis什么是RedisRedis是一个基于 C 语...">
  <meta name="author" content="东子">
  <link rel="icon" href="/images/icons/icon-29.png" type="image/png" sizes="16x16">
  <link rel="icon" href="/images/icons/icon-29@2x.png" type="image/png" sizes="32x32">
  <link rel="apple-touch-icon" href="/images/icons/icon-1024.png" sizes="180x180">
  <meta rel="mask-icon" href="/images/icons/stun-logo.svg" color="#333333">
  
    <meta rel="msapplication-TileImage" content="/images/icons/icon-1024.png">
    <meta rel="msapplication-TileColor" content="#000000">
  

  
<link rel="stylesheet" href="/css/style.css">


  
    
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1445822_p6ry5n7lrr.css">

  

  
    
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css">

  

  
    
      
        
        
<link rel="stylesheet" href="https://cdn.bootcss.com/highlight.js/9.18.1/styles/xcode.min.css" name="highlight-style" mode="light">

      
        
        
<link rel="stylesheet" href="https://cdn.bootcss.com/highlight.js/9.18.1/styles/solarized-dark.min.css" name="highlight-style" mode="dark">

      
  

  <script>
    var CONFIG = window.CONFIG || {};
    var ZHAOO = window.ZHAOO || {};
    CONFIG = {
      isHome: false,
      fancybox: true,
      pjax: false,
      loading: {
        gif: '',
        lottie: ''
      },
      lazyload: {
        enable: true,
        only_post: 'false',
        loading: {
          gif: '',
          lottie: ''
        }
      },
      donate: {
        enable: false,
        alipay: 'https://pic.izhaoo.com/alipay.jpg',
        wechat: 'https://pic.izhaoo.com/wechat.jpg'
      },
      galleries: {
        enable: false
      },
      fab: {
        enable: true,
        always_show: false
      },
      carrier: {
        enable: true
      },
      daovoice: {
        enable: false
      },
      preview: {
        background: {
          default: '',
          api: 'https://api.paugram.com/bing/'
        },
        motto: {
          default: '我在开了灯的床头下，想问问自己的心啊。',
          typing: true,
          api: 'https://v2.jinrishici.com/one.json',
          data_contents: '["data","content"]'
        },
      },
      qrcode: {
        enable: true,
        type: 'url',
        image: 'https://pic.izhaoo.com/weapp-code.jpg',
      },
      toc: {
        enable: true
      },
      scrollbar: {
        type: 'default'
      },
      notification: {
        enable: false,
        delay: 4500,
        list: '',
        page_white_list: '',
        page_black_list: ''
      },
      search: {
        enable: false,
        path: ''
      }
    }
  </script>

  

  

<meta name="generator" content="Hexo 6.3.0"></head>

<body class="lock-screen">
  <div class="loading" id="loading"></div>
  
    


  <nav class="navbar">
    <div class="left">
      
        <i class="iconfont iconhome j-navbar-back-home"></i>
      
      
        <i class="iconfont iconqrcode j-navbar-qrcode"></i>
      
      
        <i class="iconfont iconmoono" id="color-toggle" color-toggle="light"></i>
      
      
    </div>
    <div class="center">redis</div>
    <div class="right">
      <i class="iconfont iconmenu j-navbar-menu"></i>
    </div>
    
      <div id="qrcode-navbar"></div>
    
  </nav>

  
  

<nav class="menu">
  <div class="menu-container">
    <div class="menu-close">
      <i class="iconfont iconbaseline-close-px"></i>
    </div>
    <ul class="menu-content"><li class="menu-item">
        <a href="/ " class="underline "> 首页</a>
      </li><li class="menu-item">
        <a href="/archives/ " class="underline "> 归档</a>
      </li><li class="menu-item">
        <a href="/categories/ " class="underline "> 分类</a>
      </li><li class="menu-item">
        <a href="/about/ " class="underline "> 关于</a>
      </li></ul>
    
      <div class="menu-copyright"><p>Powered by <a target="_blank" href="https://gitee.com/comebackearly">东子</a>  | 爷-青结~~</p></div>
    
  </div>
</nav>
  <main id="main">
  <div class="article-wrap">
    <div class="row container">
      <div class="col-xl-3"></div>
      <div class="col-xl-6"><article class="article">
  <div class="wrap">
    <section class="head">
  <img   class="lazyload" data-original="https://pic.crazytaxii.com/Redis_Logo.png" src=""  draggable="false">
  <div class="head-mask">
    <h1 class="head-title">redis</h1>
    <div class="head-info">
      <span class="post-info-item"><i class="iconfont iconcalendar"></i>六月 04, 2022</span>
      
      <span class="post-info-item"><i class="iconfont iconfont-size"></i>5527</span>
    </div>
  </div>
</section>
    <section class="main">
      <section class="content">
        
        <h1 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a>Redis</h1><h2 id="什么是Redis"><a href="#什么是Redis" class="headerlink" title="什么是Redis"></a>什么是Redis</h2><p>Redis是一个基于 C 语言开发的开源数据库，Redis 内置了多种数据类型实现<strong>（string，list , set , zset,hash,bitmap,HyperLogLog,grospatial)<strong>，并且，Redis 还支持</strong>事务</strong> 、<strong>持久化</strong>、<strong>Lua 脚本</strong>、多种开箱即用的集群方案</p>
<blockquote>
<p>redis 在线体验 <a target="_blank" rel="noopener" href="https://try.redis.io/">Try Redis</a></p>
</blockquote>
<h2 id="Redis-为什么这么快"><a href="#Redis-为什么这么快" class="headerlink" title="Redis 为什么这么快"></a>Redis 为什么这么快</h2><ul>
<li><p>Redis 基于<strong>内存</strong>，内存的访问速度是磁盘的上千倍；</p>
</li>
<li><p>Redis 基于 <strong>Reactor</strong> 模式设计开发了一套高效的事件处理模型，主要是<strong>单线程事件循环和 IO 多路复用</strong>（Redis 线程模式）；</p>
</li>
<li><p>Redis 内置了多种优化过后的<strong>数据结构</strong>实现，性能非常高。</p>
</li>
</ul>
<h2 id="为什么使用Redis做缓存"><a href="#为什么使用Redis做缓存" class="headerlink" title="为什么使用Redis做缓存"></a>为什么使用Redis做缓存</h2><ul>
<li>高性能</li>
<li>高并发</li>
</ul>
<h2 id="Redis-实现分布式锁（TODO）"><a href="#Redis-实现分布式锁（TODO）" class="headerlink" title="Redis 实现分布式锁（TODO）"></a>Redis 实现分布式锁（TODO）</h2><h2 id="String-的应用场景有哪些？"><a href="#String-的应用场景有哪些？" class="headerlink" title="String 的应用场景有哪些？"></a>String 的应用场景有哪些？</h2><ul>
<li>常规数据（比如 session、token、、序列化后的对象）的缓存；</li>
<li>计数比如用户单位时间的请求数（简单限流可以用到）、页面单位时间的访问数；</li>
<li>分布式锁(利用 <code>**SETNX key value**</code> 命令可以实现一个最简易的分布式锁)；</li>
</ul>
<h3 id="String的底层是什么"><a href="#String的底层是什么" class="headerlink" title="String的底层是什么"></a>String的底层是什么</h3><p>但 Redis 的 String 类型的底层实现并不是 C 语言中的字符串（即以空字符 <code>\0</code> 结尾的字符数组），而是自己编写了 SSD（Simple Dynamic String，<strong>简单动态字符串</strong>） 来作为底层实现。</p>
<blockquote>
<p>对于经常变动的数据一般使用Hash类型存储</p>
</blockquote>
<h2 id="Redis-单线程模型"><a href="#Redis-单线程模型" class="headerlink" title="Redis 单线程模型"></a>Redis 单线程模型</h2><p>Redis 基于 <strong>Reactor</strong> 模式设计开发了一套高效的事件处理模型（Netty 的线程模型也基于 Reactor 模式)</p>
<p>Redis 基于 Reactor 模式开发了自己的网络事件处理器：这个处理器被称为<strong>文件事件处理器</strong></p>
<p>虽然文件事件处理器以单线程方式运行，但通过使用 <strong>I&#x2F;O 多路复用</strong>程序来<strong>监听多个套接字</strong>，文件事件处理器既实现了高性能的网络通信模型，又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接，这保持了 Redis 内部单线程设计的简单性。</p>
<h3 id="既然是单线程，那怎么监听大量的客户端连接呢？"><a href="#既然是单线程，那怎么监听大量的客户端连接呢？" class="headerlink" title="既然是单线程，那怎么监听大量的客户端连接呢？"></a><strong>既然是单线程，那怎么监听大量的客户端连接呢？</strong></h3><p>Redis 通过 <strong>IO 多路复用程序</strong> 来监听来自客户端的大量连接（或者说是监听多个 socket），它会将感兴趣的事件及类型（读、写）注册到内核中并监听每个事件是否发生。</p>
<p>文件事件处理器（file event handler）主要是包含 4 个部分：</p>
<p><img   class="lazyload" data-original="https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/database/redis/redis-event-handler.png" src=""  alt="文件事件处理器"></p>
<h2 id="Redis-键过期"><a href="#Redis-键过期" class="headerlink" title="Redis 键过期"></a>Redis 键过期</h2><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0.0.1:6379</span>&gt; expire key <span class="number">60</span> # 数据在 60s 后过期</span><br><span class="line">(integer) <span class="number">1</span></span><br><span class="line"><span class="number">127.0.0.1:6379</span>&gt; setex key <span class="number">60</span> value # 数据在 60s 后过期 (setex:<span class="string">[set]</span> + <span class="string">[ex]</span>pire)</span><br><span class="line">OK</span><br><span class="line"><span class="number">127.0.0.1:6379</span>&gt; ttl key # 查看数据还有多久过期</span><br><span class="line">(integer) <span class="number">56</span></span><br></pre></td></tr></table></figure>

<h3 id="Redis-是如何判断数据是否过期的呢？"><a href="#Redis-是如何判断数据是否过期的呢？" class="headerlink" title="Redis 是如何判断数据是否过期的呢？"></a>Redis 是如何判断数据是否过期的呢？</h3><p>Redis 通过一个叫做过期字典（可以看作是 hash 表）来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键)，过期字典的值是一个 long long 类型的整数，这个整数保存了 key 所指向的数据库键的<strong>过期时间</strong>（毫秒精度的 UNIX 时间戳）</p>
<p>过期字典是存储在 redisDb 这个结构里的：</p>
<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">typedef <span class="keyword">struct</span> <span class="keyword">redisDb</span> &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">dict</span> <span class="operator">*</span><span class="built_in">dict</span>;     <span class="comment">//数据库键空间,保存着数据库中所有键值对</span></span><br><span class="line">    <span class="built_in">dict</span> <span class="operator">*</span>expires   <span class="comment">// 过期字典,保存着键的过期时间</span></span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">&#125; redisDb;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="过期的数据的删除策略"><a href="#过期的数据的删除策略" class="headerlink" title="过期的数据的删除策略"></a>过期的数据的删除策略</h3><ul>
<li><strong>惰性删除</strong>： 只会在取出 key 的时候才对数据进行过期检查</li>
<li><strong>定期删除</strong>： 每隔一段时间抽取一批 key 执行删除过期 key 操作</li>
</ul>
<blockquote>
<p>  Redis 采用的是 <strong>定期删除+惰性&#x2F;懒汉式删除</strong></p>
</blockquote>
<h3 id="Redis-内存淘汰机制"><a href="#Redis-内存淘汰机制" class="headerlink" title="Redis 内存淘汰机制"></a>Redis 内存淘汰机制</h3><p>Redis 提供 6 种数据淘汰策略:</p>
<p>从已设置过期时间的数据集中挑选最近最少使用的数据淘汰</p>
<p>从已设置过期时间的数据集中挑选<strong>将要过期</strong>的数据淘汰</p>
<p>从<strong>已设置过期时间的数据集中任意选择</strong>数据淘汰</p>
<p>当内存不足以容纳新写入数据时，在键空间中，<strong>移除最近最少使用的 key</strong>（这个是最常用的）<strong>LRU</strong></p>
<p>从<strong>数据集中任意选择数据淘汰</strong></p>
<p>禁止驱逐数据，也就是说当内存不足以容纳新写入数据时，新写入操作会<strong>报错</strong>。</p>
<h2 id="Redis的key过期了，会不会被立即删除"><a href="#Redis的key过期了，会不会被立即删除" class="headerlink" title="Redis的key过期了，会不会被立即删除"></a><strong>Redis的key过期了，会不会被立即删除</strong></h2><p>不会，redis对于过期的key采用了两种策略：</p>
<p>​						 											1.<strong>轮询删除</strong>（轮询判断哪些 key过期了）</p>
<pre><code>                                                                 2.**惰性删除**（在被访问到的时候才删除key）
</code></pre>
<h2 id="Redis-持久化机制"><a href="#Redis-持久化机制" class="headerlink" title="Redis 持久化机制"></a>Redis 持久化机制</h2><ul>
<li>RDB</li>
<li>AOF</li>
</ul>
<h3 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h3><p>Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本,Redis 提供了两个命令来生成 RDB 快照文件：</p>
<ul>
<li><code>save</code> : 主线程执行，会阻塞主线程；</li>
<li><code>bgsave</code> : 子线程执行，不会阻塞主线程，默认选项</li>
</ul>
<blockquote>
<p>默认情况下就是RDB</p>
</blockquote>
<h3 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h3><p>与快照持久化相比，AOF 持久化的<strong>实时性更好</strong></p>
<p> Redis AOF 持久化机制是在<strong>执行完命令之后</strong>（避免额外的检查开销，AOF 记录日志不会对命令进行语法检查）记录日志,通过日志恢复数据</p>
<p><img   class="lazyload" data-original="https://javaguide.cn/assets/redis-aof-write-log-disc.ca22bd65.png" src=""  alt="AOF 记录日志过程"></p>
<h3 id="AOF重写"><a href="#AOF重写" class="headerlink" title="AOF重写"></a>AOF重写</h3><p>当 AOF 变得太大时，Redis 能够在后台自动重写 AOF 产生一个新的 AOF 文件，这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样，但体积更小。</p>
<h3 id="选择-RDB-和-AOF-RDB和AOF的优缺点？"><a href="#选择-RDB-和-AOF-RDB和AOF的优缺点？" class="headerlink" title="选择 RDB 和 AOF,RDB和AOF的优缺点？"></a>选择 RDB 和 AOF,RDB和AOF的优缺点？</h3><p>RDB 文件存储的内容是经过压缩的二进制数据，<strong>恢复快，容量小。</strong> </p>
<p>RDB 的数据安全性不如 AOF，没有办法实时或者<strong>秒级持久化</strong>数据。</p>
<h2 id="Redis事务"><a href="#Redis事务" class="headerlink" title="Redis事务"></a>Redis事务</h2><p>Redis 可以通过 <strong><code>MULTI</code>，<code>EXEC</code>，<code>DISCARD</code> 和 <code>WATCH</code></strong> 等命令来实现事务(transaction)功能</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">MULTI</span><br><span class="line">OK</span><br><span class="line"><span class="built_in">SET</span> PROJECT <span class="string">&quot;JavaGuide&quot;</span></span><br><span class="line">QUEUED</span><br><span class="line"><span class="built_in">GET</span> PROJECT</span><br><span class="line">QUEUED</span><br><span class="line">EXEC</span><br><span class="line">1) OK</span><br><span class="line">2) <span class="string">&quot;JavaGuide&quot;</span></span><br></pre></td></tr></table></figure>

<p><strong>WATCH</strong> 命令监听指定的 Key，当调用 <code>EXEC</code> 命令执行事务时，如果一个被 <code>WATCH</code> 命令监视的 Key 被 <strong>其他客户端&#x2F;Session</strong> 修改的话，整个事务都不会被执行。</p>
<h3 id="Redis事务不支持原子性"><a href="#Redis事务不支持原子性" class="headerlink" title="Redis事务不支持原子性"></a>Redis事务不支持原子性</h3><p>Redis 事务在运行错误的情况下，<strong>除了执行过程中出现错误的命令外，其他命令都能正常执行</strong>。并且，Redis 事务是<strong>不支持回滚</strong>（roll back）操作的。因此，Redis 事务其实是不满足原子性的（而且不满足持久性）</p>
<blockquote>
<p>Redis 事务提供了一种将多个命令请求打包的功能。然后，再按顺序执行打包的所有命令，并且不会被中途打断。</p>
</blockquote>
<h3 id="如何解决-Redis-事务的缺陷？"><a href="#如何解决-Redis-事务的缺陷？" class="headerlink" title="如何解决 Redis 事务的缺陷？"></a>如何解决 Redis 事务的缺陷？</h3><p>我们可以利用 <strong>Lua 脚本</strong>来批量执行多条 Redis 命令，这些 Redis 命令会被提交到 Redis 服务器一次性执行完成，大幅减小了网络开销。</p>
<p>一段 Lua 脚本可以视作一条命令执行，<strong>一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行</strong>，保证了操作不会被其他指令插入或打扰。</p>
<h2 id="Redis性能优化"><a href="#Redis性能优化" class="headerlink" title="Redis性能优化"></a>Redis性能优化</h2><p><strong>定期删除执行过程中，如果突然遇到大量过期 key 的话，客户端请求必须等待定期清理过期 key 任务线程执行完成，因为这个这个定期任务线程是在 Redis 主线程中执行的。这就导致客户端请求没办法被及时处理，响应速度会比较慢。</strong></p>
<p>如何解决：</p>
<ol>
<li>给 key 设置随机过期时间。</li>
<li>开启 lazy-free（惰性删除&#x2F;延迟释放） 。lazy-free 指的是让 Redis 采用异步方式延迟释放 key 使用的内存，将该操作交给单独的子线程处理，避免阻塞主线程。</li>
</ol>
<h2 id="Redis生产问题"><a href="#Redis生产问题" class="headerlink" title="Redis生产问题"></a>Redis生产问题</h2><h3 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h3><blockquote>
<p>查询不存在的数据</p>
</blockquote>
<p>缓存穿透说简单点就是大量请求的 key 是不合理的，<strong>根本不存在于缓存中，也不存在于数据库中</strong> 。这就导致这些请求直接到了数据库上，根本没有经过缓存这一层，对数据库造成了巨大的压力，可能直接就被这么多请求弄宕机了。</p>
<ul>
<li><p>缓存空值：缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间</p>
</li>
<li><p>布隆过滤器：布隆过滤器是一个非常神奇的<strong>数据结构</strong>，通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们需要的就是判断 key 是否合法。</p>
<blockquote>
<p><strong>布隆过滤器说某个元素存在，小概率会误判。布隆过滤器说某个元素不在，那么这个元素一定不在。</strong></p>
</blockquote>
</li>
</ul>
<h3 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h3><blockquote>
<p>热点数据过期了</p>
</blockquote>
<p>请求的 key 对应的是 <strong>热点数据</strong> ，该数据 <strong>存在于数据库中，但不存在于缓存中（通常是因为缓存中的那份数据已经过期）</strong> </p>
<ul>
<li>设置<strong>热点数据永不过期</strong>或者过期时间比较长。</li>
<li>请求数据库写数据到缓存之前，先获取互斥锁，保证只有一个请求会落到数据库上，减少数据库的压力</li>
</ul>
<h3 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h3><blockquote>
<p>缓存设置了相同的过期时间</p>
</blockquote>
<p><strong>缓存在同一时间大面积的失效，导致大量的请求都直接落到了数据库上，对数据库造成了巨大的压力。</strong> 这就好比雪崩一样，可能直接就被这么多请求弄宕机了。</p>
<ol>
<li>设置不同的失效时间比如<strong>随机设置缓存的失效时间。</strong></li>
<li>设置<strong>二级缓存</strong></li>
</ol>
<h2 id="如何保证缓存和数据库数据的一致性？TODO"><a href="#如何保证缓存和数据库数据的一致性？TODO" class="headerlink" title="如何保证缓存和数据库数据的一致性？TODO"></a>如何保证缓存和数据库数据的一致性？TODO</h2><h2 id="实际应用"><a href="#实际应用" class="headerlink" title="实际应用"></a>实际应用</h2><h3 id="redis中的zset实现排行榜应用实现，取Top-N测试"><a href="#redis中的zset实现排行榜应用实现，取Top-N测试" class="headerlink" title="redis中的zset实现排行榜应用实现，取Top N测试"></a>redis中的zset实现排行榜应用实现，取Top N测试</h3><p>使用redis的sorted set实现排行榜</p>
<p><code>ZRANGE</code> (从小到大排序) 、 <code>ZREVRANGE</code> （从大到小排序）、<code>ZREVRANK</code> (指定元素排名)。</p>
<figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">添加一个成员和对应的分数</span><br><span class="line">ZADD leaderboard <span class="number">1000</span> <span class="string">&quot;Alice&quot;</span></span><br><span class="line">获取某个成员的排名</span><br><span class="line">ZRANK leaderboard <span class="string">&quot;Alice&quot;</span></span><br><span class="line">获取排名在某个范围内的成员列表</span><br><span class="line">ZRANGE leaderboard <span class="number">0</span> <span class="number">9</span> WITHSCORES</span><br><span class="line">获取排名最高的<span class="built_in">N</span>个成员列表</span><br><span class="line">ZREVRANGE leaderboard <span class="number">0</span> <span class="built_in">N</span>-<span class="number">1</span> WITHSCORES</span><br></pre></td></tr></table></figure>

<p>这个命令将返回排名最高的N个成员列表，并附带每个成员的分数。为了测试排行榜应用，可以先添加一些成员和对应的分数，然后使用命令获取排名和分数，最后使用命令获取排名最高的N个成员列表。可以使用Redis的客户端工具，如redis-cli或者redis-py来执行以上命令。</p>
<h3 id="redis-Hyperloglog实现记录网站访问量"><a href="#redis-Hyperloglog实现记录网站访问量" class="headerlink" title="redis Hyperloglog实现记录网站访问量"></a>redis Hyperloglog实现记录网站访问量</h3><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">创建一个HyperLogLog</span><br><span class="line">PFADD website-visits <span class="number">192.168.1.1</span> <span class="number">192.168.1.2</span> <span class="number">192.168.1.3</span> #可存储id</span><br><span class="line">添加一个新的IP地址到HyperLogLog中，从而更新网站的访问量。</span><br><span class="line">PFADD website-visits <span class="number">192.168.1.4</span></span><br><span class="line">HyperLogLog的基数估计</span><br><span class="line">PFCOUNT website-visits</span><br></pre></td></tr></table></figure>

<p>使用Redis的HyperLogLog可以非常方便地记录网站的访问量，并且<strong>占用非常小的内存空间</strong>。但需要注意的是，由于HyperLogLog是基于概率算法实现的，因此其结果具有一定的误差，因此在实际应用中需要进行合理的基数估计和误差控制。</p>
<h3 id="使用-Set-实现抽奖系统需要用到什么命令？"><a href="#使用-Set-实现抽奖系统需要用到什么命令？" class="headerlink" title="使用 Set 实现抽奖系统需要用到什么命令？"></a>使用 Set 实现抽奖系统需要用到什么命令？</h3><p><code>SPOP key count</code> ： 随机移除并获取指定集合中一个或多个元素，适合不允许重复中奖的场景。</p>
<p><code>SRANDMEMBER key count</code> : 随机获取指定集合中指定数量的元素，适合允许重复中奖的场景</p>
<figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">SADD 命令：将一个或多个元素添加到集合中。例如，将用户 <span class="string">&quot;Alice&quot;</span>、<span class="string">&quot;Bob&quot;</span>、<span class="string">&quot;Charlie&quot;</span>、<span class="string">&quot;David&quot;</span> 和 <span class="string">&quot;Emma&quot;</span> 添加到名为 <span class="string">&quot;lottery_users&quot;</span> 的集合中，命令为 SADD lottery_users Alice <span class="keyword">Bob </span>Charlie David Emma。</span><br><span class="line"></span><br><span class="line"><span class="keyword">SRANDMEMBER </span>命令：随机获取集合中的一个元素。例如，从名为 <span class="string">&quot;lottery_users&quot;</span> 的集合中随机获取一个中奖用户，命令为 <span class="keyword">SRANDMEMBER </span>lottery_users。</span><br><span class="line"></span><br><span class="line">SREM 命令：从集合中删除一个或多个元素。例如，将名为 <span class="string">&quot;lottery_users&quot;</span> 的集合中的中奖用户删除，命令为 SREM lottery_users 中奖用户。</span><br></pre></td></tr></table></figure>

<h3 id="使用-Bitmap-统计活跃用户怎么做？"><a href="#使用-Bitmap-统计活跃用户怎么做？" class="headerlink" title="使用 Bitmap 统计活跃用户怎么做？"></a>使用 Bitmap 统计活跃用户怎么做？</h3><p>假设现在有 4 个用户活跃，它们的 ID 分别为 1001、2002、3003 和 4004，那么可以依次执行以下命令：</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">SETBIT</span> active_users <span class="number">1001</span> <span class="number">1</span></span><br><span class="line"><span class="attribute">SETBIT</span> active_users <span class="number">2002</span> <span class="number">1</span></span><br><span class="line"><span class="attribute">SETBIT</span> active_users <span class="number">3003</span> <span class="number">1</span></span><br><span class="line"><span class="attribute">SETBIT</span> active_users <span class="number">4004</span> <span class="number">1</span></span><br><span class="line"><span class="attribute">BITCOUNT</span> active_users</span><br></pre></td></tr></table></figure>

<p>执行最后一个命令后，即可得到当前活跃用户数。</p>

      </section>
      <section class="extra">
        
          <ul class="copyright">
  
    <li><strong>本文作者：</strong>东子</li>
    <li><strong>本文链接：</strong><a href="https://comebackearly.gitee.io/2022/06/04/redis/index.html" title="https:&#x2F;&#x2F;comebackearly.gitee.io&#x2F;2022&#x2F;06&#x2F;04&#x2F;redis&#x2F;index.html">https:&#x2F;&#x2F;comebackearly.gitee.io&#x2F;2022&#x2F;06&#x2F;04&#x2F;redis&#x2F;index.html</a></li>
    <li><strong>版权声明：</strong>本博客所有文章均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" title="BY-NC-SA" target="_blank" rel="noopener">BY-NC-SA</a> 许可协议，转载请注明出处！</li>
  
</ul>
        
        
        
        
  <nav class="nav">
    <a href="/2022/06/04/java%E5%9F%BA%E7%A1%80%E9%81%97%E5%BF%98%E9%A2%98%E7%9B%AE/"><i class="iconfont iconleft"></i>java基础</a>
    <a href="/2022/06/03/Mysql/">mysql<i class="iconfont iconright"></i></a>
  </nav>

      </section>
      
        <section class="comments">
  
    <div class="btn" id="comments-btn">查看评论</div>
  
  
<div id="valine"></div>
<script defer src="//unpkg.com/valine/dist/Valine.min.js"></script>
<script>
  window.onload = function () {
    var loadValine = function () {
      new Valine({
        el: '#valine',
        app_id: "833C0IwLe9o8QE1pN25Mv59F-gzGzoHsz",
        app_key: "YLQZdn7egrZOmJnwr6YFEw0y",
        placeholder: "雁过留痕",
        avatar: "mp",
        pageSize: "10",
        lang: "zh-CN",
      });
    }
    if ( true ) {
      $("#comments-btn").on("click", function () {
        $(this).hide();
        loadValine();
      });
    } else {
      loadValine();
    }
  };
</script>

</section>
      
    </section>
  </div>
</article></div>
      <div class="col-xl-3">
        
          
  <aside class="toc-wrap">
    <h3 class="toc-title">文章目录：</h3>
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Redis"><span class="toc-text">Redis</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFRedis"><span class="toc-text">什么是Redis</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB"><span class="toc-text">Redis 为什么这么快</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8Redis%E5%81%9A%E7%BC%93%E5%AD%98"><span class="toc-text">为什么使用Redis做缓存</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis-%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%EF%BC%88TODO%EF%BC%89"><span class="toc-text">Redis 实现分布式锁（TODO）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#String-%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F"><span class="toc-text">String 的应用场景有哪些？</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#String%E7%9A%84%E5%BA%95%E5%B1%82%E6%98%AF%E4%BB%80%E4%B9%88"><span class="toc-text">String的底层是什么</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis-%E5%8D%95%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B"><span class="toc-text">Redis 单线程模型</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A2%E7%84%B6%E6%98%AF%E5%8D%95%E7%BA%BF%E7%A8%8B%EF%BC%8C%E9%82%A3%E6%80%8E%E4%B9%88%E7%9B%91%E5%90%AC%E5%A4%A7%E9%87%8F%E7%9A%84%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%BF%9E%E6%8E%A5%E5%91%A2%EF%BC%9F"><span class="toc-text">既然是单线程，那怎么监听大量的客户端连接呢？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis-%E9%94%AE%E8%BF%87%E6%9C%9F"><span class="toc-text">Redis 键过期</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis-%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E6%95%B0%E6%8D%AE%E6%98%AF%E5%90%A6%E8%BF%87%E6%9C%9F%E7%9A%84%E5%91%A2%EF%BC%9F"><span class="toc-text">Redis 是如何判断数据是否过期的呢？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%87%E6%9C%9F%E7%9A%84%E6%95%B0%E6%8D%AE%E7%9A%84%E5%88%A0%E9%99%A4%E7%AD%96%E7%95%A5"><span class="toc-text">过期的数据的删除策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis-%E5%86%85%E5%AD%98%E6%B7%98%E6%B1%B0%E6%9C%BA%E5%88%B6"><span class="toc-text">Redis 内存淘汰机制</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E7%9A%84key%E8%BF%87%E6%9C%9F%E4%BA%86%EF%BC%8C%E4%BC%9A%E4%B8%8D%E4%BC%9A%E8%A2%AB%E7%AB%8B%E5%8D%B3%E5%88%A0%E9%99%A4"><span class="toc-text">Redis的key过期了，会不会被立即删除</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6"><span class="toc-text">Redis 持久化机制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#RDB"><span class="toc-text">RDB</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AOF"><span class="toc-text">AOF</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#AOF%E9%87%8D%E5%86%99"><span class="toc-text">AOF重写</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%89%E6%8B%A9-RDB-%E5%92%8C-AOF-RDB%E5%92%8CAOF%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9%EF%BC%9F"><span class="toc-text">选择 RDB 和 AOF,RDB和AOF的优缺点？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E4%BA%8B%E5%8A%A1"><span class="toc-text">Redis事务</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Redis%E4%BA%8B%E5%8A%A1%E4%B8%8D%E6%94%AF%E6%8C%81%E5%8E%9F%E5%AD%90%E6%80%A7"><span class="toc-text">Redis事务不支持原子性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3-Redis-%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%BC%BA%E9%99%B7%EF%BC%9F"><span class="toc-text">如何解决 Redis 事务的缺陷？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"><span class="toc-text">Redis性能优化</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Redis%E7%94%9F%E4%BA%A7%E9%97%AE%E9%A2%98"><span class="toc-text">Redis生产问题</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F"><span class="toc-text">缓存穿透</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF"><span class="toc-text">缓存击穿</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9"><span class="toc-text">缓存雪崩</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E7%BC%93%E5%AD%98%E5%92%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E6%95%B0%E6%8D%AE%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7%EF%BC%9FTODO"><span class="toc-text">如何保证缓存和数据库数据的一致性？TODO</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8"><span class="toc-text">实际应用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#redis%E4%B8%AD%E7%9A%84zset%E5%AE%9E%E7%8E%B0%E6%8E%92%E8%A1%8C%E6%A6%9C%E5%BA%94%E7%94%A8%E5%AE%9E%E7%8E%B0%EF%BC%8C%E5%8F%96Top-N%E6%B5%8B%E8%AF%95"><span class="toc-text">redis中的zset实现排行榜应用实现，取Top N测试</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#redis-Hyperloglog%E5%AE%9E%E7%8E%B0%E8%AE%B0%E5%BD%95%E7%BD%91%E7%AB%99%E8%AE%BF%E9%97%AE%E9%87%8F"><span class="toc-text">redis Hyperloglog实现记录网站访问量</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8-Set-%E5%AE%9E%E7%8E%B0%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F%E9%9C%80%E8%A6%81%E7%94%A8%E5%88%B0%E4%BB%80%E4%B9%88%E5%91%BD%E4%BB%A4%EF%BC%9F"><span class="toc-text">使用 Set 实现抽奖系统需要用到什么命令？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8-Bitmap-%E7%BB%9F%E8%AE%A1%E6%B4%BB%E8%B7%83%E7%94%A8%E6%88%B7%E6%80%8E%E4%B9%88%E5%81%9A%EF%BC%9F"><span class="toc-text">使用 Bitmap 统计活跃用户怎么做？</span></a></li></ol></li></ol></li></ol>
  </aside>

        
      </div>
    </div>
  </div>
</main>
  

<footer class="footer">
  <div class="footer-social"><a 
        href="tencent://message/?Menu=yes&uin=2911890696 "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#12B7F5'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  iconQQ "></i>
      </a><a 
        href="javascript:; "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#09BB07'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  iconwechat-fill "></i>
      </a><a 
        href="https://github.com/nuban "
        target="_blank"
        class="footer-social-item"
        onMouseOver="this.style.color= '#9f7be1'" 
        onMouseOut="this.style.color='#33333D'">
          <i class="iconfont  icongithub-fill "></i>
      </a></div>
  
    <div class="footer-copyright"><p>Powered by <a target="_blank" href="https://gitee.com/comebackearly">东子</a>  | 爷-青结~~</p></div>
  
  <link rel="stylesheet" href="/dist/APlayer.min.css">
  <div id="aplayer"></div>
  <script type="text/javascript" src="/dist/APlayer.min.js"></script>
  <script type="text/javascript" src="/dist/music.js"></script>
  
</footer>
  
      <div class="fab fab-plus">
    <i class="iconfont iconplus"></i>
  </div>
  
  
  
  <div class="fab fab-up">
    <i class="iconfont iconcaret-up"></i>
  </div>
  
  
  
    
<script src="/js/color-mode.js"></script>

  
  
</body>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>





  
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.lazyload/1.9.1/jquery.lazyload.min.js"></script>




  
<script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js"></script>






  
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>




<script src="/js/utils.js"></script>
<script src="/js/script.js"></script>







  <script>
    (function () {
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
      } else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
      }
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>



  <script defer src='https://hm.baidu.com/hm.js?dda12e485d1b1d3113db8147f482111c'></script>











</html>